package com.xzzz.irda.guardcat.server.alert;

import cn.hutool.core.annotation.AnnotationUtil;
import cn.hutool.core.util.ClassUtil;
import com.xzzz.common.base.pojo.PageRes;
import com.xzzz.common.base.util.spring.SpringUtil;
import com.xzzz.common.base.util.thread.NamedThreadFactory;
import com.xzzz.irda.guardcat.core.constants.GuardcatConstant;
import com.xzzz.irda.guardcat.core.pojo.Alert;
import com.xzzz.irda.guardcat.server.setting.AlertSetting;
import com.xzzz.irda.guardcat.server.setting.AlertSettingManager;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.*;

/**
 * @author xzzz
 */
@Slf4j
public abstract class AbstractAlertManager {

    /**
     * 警告的监听方法和方法所在的类
     */
    private static final Map<Method, Object> ALERT_LISTENER_METHOD_INSTANCE = new HashMap<>();

    private static volatile AlertSettingManager ALERT_SETTING_MANAGER;

    private static final ExecutorService PUBLISH_EXECUTOR;

    /*
     * 初始化监听器类
     */
    static {
        Set<Class<?>> allClass = ClassUtil.scanPackage(GuardcatConstant.SERVER_LISTENER_PACKAGE);
        for (Class<?> clazz : allClass) {
            for (Method method : clazz.getMethods()) {
                AlertListener alertListener = AnnotationUtil.getAnnotation(method, AlertListener.class);
                if (alertListener != null) {

                    Class<?>[] classes = method.getParameterTypes();
                    if (classes.length != 2) {
                        log.warn("{} 监听类方法 {} 参数个数错误", clazz.getSimpleName(), method.getName());
                        continue;
                    }

                    if (!ClassUtil.equals(classes[0], Alert.class.getName(), true)) {
                        log.warn("{}.{} 的第一个参数必须为 {}", clazz.getSimpleName(), method.getName(), Alert.class.getName());
                    }

                    if (!ClassUtil.equals(classes[1], AlertSetting.class.getName(), true)) {
                        log.warn("{}.{} 的第二个参数必须为 {}", clazz.getSimpleName(), method.getName(), AlertSetting.class.getName());
                    }

                    try {
                        if (null == ALERT_LISTENER_METHOD_INSTANCE.get(method)) {
                            ALERT_LISTENER_METHOD_INSTANCE.put(method, clazz.newInstance());
                        }

                    } catch (InstantiationException | IllegalAccessException e) {
                        e.printStackTrace();
                    }
                }
            }
        }

        int cores = Runtime.getRuntime().availableProcessors();
        long keepAliveTime = 0;
        int queueSize = 1024;
        RejectedExecutionHandler handler = new ThreadPoolExecutor.DiscardPolicy();
        PUBLISH_EXECUTOR = new ThreadPoolExecutor(cores, cores,
                keepAliveTime, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<>(queueSize),
                new NamedThreadFactory("guardcat-server-alert-publish"), handler);

    }

    /**
     * 获取警告配置管理器
     */
    private AlertSettingManager getSettingManager() {
        if (ALERT_SETTING_MANAGER == null) {
            synchronized (AlertSettingManager.class) {
                if (ALERT_SETTING_MANAGER == null) {
                    ALERT_SETTING_MANAGER = SpringUtil.getBean(AlertSettingManager.class);
                }
            }
        }
        return ALERT_SETTING_MANAGER;
    }

    /**
     * 警告分页
     *
     * @param appName   应用名称
     * @param machineId 机器ID
     * @param id        警告ID
     * @param read      是否已读
     * @param pageSize  每页条数
     * @param pageNum   当前页数
     * @return 列表
     */
    public abstract PageRes<Alert> pages(String appName, String machineId, String id, Boolean read, Integer pageSize, Integer pageNum);

    /**
     * 应用警告直方图
     *
     * @param appName   应用名称
     * @param machineId 机器ID
     * @return 直方图信息
     */
    public abstract Map<String, Object[]> histogram(String appName, String machineId);


    /**
     * 新增并发布警告
     *
     * @param alert 警告信息
     */
    public void addAndPublish(final Alert alert) {
        this.add(alert);
        final AlertSetting alertSetting = getSettingManager().get(alert.getAppName());
        // 如果消息不通知, 则不发送
        if (alertSetting != null && alert.getNotification()) {
            PUBLISH_EXECUTOR.submit(() -> this.publish(alert, alertSetting));
        }
    }

    /**
     * 新增警告
     *
     * @param alert 警告信息
     */
    public abstract void add(Alert alert);

    /**
     * 发布警告
     *
     * @param alert 警告信息
     */
    public final void publish(final Alert alert, final AlertSetting alertSetting) {
        ALERT_LISTENER_METHOD_INSTANCE.forEach((method, instance) -> {
            try {
                method.invoke(instance, alert, alertSetting);
            } catch (IllegalAccessException | InvocationTargetException e) {
                log.error("调用警告监听方法[{}]异常:{}", instance.getClass().getName() + "." + method.getName(), e.getMessage());
            }
        });
    }

    /**
     * 忽略某条警告
     *
     * @param alertId 警告ID
     */
    public abstract void read(String alertId);

    /**
     * 淘汰警告
     */
    public abstract void expire();

    /**
     * 统计机器的警告数量
     *
     * @param machineId 机器ID
     * @param read      查询忽略的还是未忽略的,默认查询未忽略的警告
     * @return 警告数量
     */
    public abstract long countMachine(String machineId, Boolean read);

    /**
     * 统计多台机器的警告数量
     *
     * @param machineIds 机器ID集合
     * @param read       查询忽略的还是未忽略的,默认查询未忽略的警告
     * @return 警告数量
     */
    public abstract Map<String, Long> countMachine(Collection<String> machineIds, Boolean read);

    /**
     * 警告条数
     *
     * @param appName 应用名称
     * @param read    查询忽略的还是未忽略的,默认查询未忽略的警告
     * @return 查询忽略的还是未忽略的, 默认查询未忽略的警告
     */
    public abstract long countApp(String appName, Boolean read);

}
